Optimeerige oma Reacti rakendusi useState'i abil. Õppige täiustatud tehnikaid tõhusaks olekuhalduseks ja jõudluse parandamiseks.
React useState: Oleku hook'i optimeerimisstrateegiate valdamine
useState Hook on Reactis komponentide oleku haldamiseks põhiline ehituskivi. Kuigi see on uskumatult mitmekülgne ja lihtne kasutada, võib selle ebaõige kasutamine põhjustada jõudluse kitsaskohti, eriti keerukates rakendustes. See põhjalik juhend uurib täiustatud strateegiaid useState'i optimeerimiseks, et tagada teie Reacti rakenduste jõudlus ja hooldatavus.
useState'i ja selle mõjude mõistmine
Enne optimeerimistehnikatesse süvenemist kordame üle useState'i põhitõed. useState Hook võimaldab funktsionaalsetel komponentidel omada olekut. See tagastab olekumuutuja ja funktsiooni selle muutuja uuendamiseks. Iga kord, kui olek uueneb, renderdatakse komponent uuesti.
Põhinäide:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
Loendur: {count}
);
}
export default Counter;
Selles lihtsas näites uuendab nupule "Suurenda" klõpsamine count'i olekut, käivitades Counter komponendi uuesti renderdamise. Kuigi see töötab väikeste komponentide puhul suurepäraselt, võivad kontrollimatud uuesti renderdamised suuremates rakendustes jõudlust tõsiselt mõjutada.
Miks optimeerida useState'i?
Mittevajalikud uuesti renderdamised on peamine süüdlane Reacti rakenduste jõudlusprobleemide taga. Iga uuesti renderdamine tarbib ressursse ja võib viia loiuni kasutajakogemuseni. useState'i optimeerimine aitab:
- Vähendada mittevajalikke uuesti renderdamisi: Vältida komponentide uuesti renderdamist, kui nende olek pole tegelikult muutunud.
- Parandada jõudlust: Muuta teie rakendus kiiremaks ja reageerivamaks.
- Tõhustada hooldatavust: Kirjutada puhtamat ja tõhusamat koodi.
Optimeerimisstrateegia 1: Funktsionaalsed uuendused
Kui uuendate olekut eelmise oleku põhjal, kasutage alati setCount'i funktsionaalset vormi. See hoiab ära probleeme aegunud closure'itega ja tagab, et töötate kõige ajakohasema olekuga.
Vale (potentsiaalselt problemaatiline):
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setTimeout(() => {
setCount(count + 1); // Potentsiaalselt aegunud 'count' väärtus
}, 1000);
};
return (
Loendur: {count}
);
}
Õige (funktsionaalne uuendus):
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setTimeout(() => {
setCount(prevCount => prevCount + 1); // Tagab õige 'count' väärtuse
}, 1000);
};
return (
Loendur: {count}
);
}
Kasutades setCount(prevCount => prevCount + 1), edastate funktsiooni setCount'ile. React paneb seejärel oleku uuenduse järjekorda ja täidab funktsiooni kõige värskema olekuväärtusega, vältides aegunud closure'i probleemi.
Optimeerimisstrateegia 2: Muutumatud oleku uuendused
Kui tegelete oma olekus objektide või massiividega, uuendage neid alati muutumatul viisil. Oleku otsene muteerimine ei käivita uuesti renderdamist, sest React tugineb muutuste tuvastamiseks viidete võrdsusele. Selle asemel looge objektist või massiivist uus koopia koos soovitud muudatustega.
Vale (oleku muteerimine):
function ShoppingCart() {
const [items, setItems] = useState([{ id: 1, name: 'Apple', quantity: 2 }]);
const updateQuantity = (id, newQuantity) => {
const item = items.find(item => item.id === id);
if (item) {
item.quantity = newQuantity; // Otsene muteerimine! Ei käivita uuesti renderdamist.
setItems(items); // See tekitab probleeme, sest React ei tuvasta muutust.
}
};
return (
{items.map(item => (
{item.name} - Kogus: {item.quantity}
))}
);
}
Õige (muutumatu uuendus):
function ShoppingCart() {
const [items, setItems] = useState([{ id: 1, name: 'Apple', quantity: 2 }]);
const updateQuantity = (id, newQuantity) => {
setItems(prevItems =>
prevItems.map(item =>
item.id === id ? { ...item, quantity: newQuantity } : item
)
);
};
return (
{items.map(item => (
{item.name} - Kogus: {item.quantity}
))}
);
}
Parandatud versioonis kasutame .map()'i, et luua uus massiiv uuendatud elemendiga. Hajusoperaatorit (...item) kasutatakse uue objekti loomiseks olemasolevate omadustega ja seejärel kirjutame quantity omaduse üle uue väärtusega. See tagab, et setItems saab uue massiivi, käivitades uuesti renderdamise ja uuendades kasutajaliidest.
Optimeerimisstrateegia 3: useMemo kasutamine mittevajalike uuesti renderdamiste vältimiseks
useMemo hook'i saab kasutada arvutuse tulemuse memoiseerimiseks. See on kasulik, kui arvutus on kallis ja sõltub ainult teatud olekumuutujatest. Kui need olekumuutujad pole muutunud, tagastab useMemo vahemällu salvestatud tulemuse, vältides arvutuse uuesti käivitamist ja mittevajalikke uuesti renderdamisi.
Näide:
import React, { useState, useMemo } from 'react';
function ExpensiveComponent({ data }) {
const [multiplier, setMultiplier] = useState(2);
// Kallis arvutus, mis sõltub ainult 'data'-st ja 'multiplier'-ist
const processedData = useMemo(() => {
console.log('Andmete töötlemine...');
// Simuleeri kallist operatsiooni
let result = data.map(item => item * multiplier);
return result;
}, [data, multiplier]);
return (
Töödeldud andmed: {processedData.join(', ')}
);
}
function App() {
const [data, setData] = useState([1, 2, 3, 4, 5]);
return (
);
}
export default App;
Selles näites arvutatakse processedData uuesti ainult siis, kui data või multiplier muutub. Kui ExpensiveComponent'i teised oleku osad muutuvad, renderdatakse komponent uuesti, kuid processedData't ei arvutata uuesti, säästes töötlemisaega.
Optimeerimisstrateegia 4: useCallback kasutamine funktsioonide memoiseerimiseks
Sarnaselt useMemo'ga memoiseerib useCallback funktsioone. See on eriti kasulik, kui funktsioone edastatakse props'idena alamkomponentidele. Ilma useCallback'ita luuakse igal renderdamisel uus funktsiooni eksemplar, mis põhjustab alamkomponendi uuesti renderdamise isegi siis, kui selle props'id pole tegelikult muutunud. See on sellepärast, et React kontrollib props'ide erinevust range võrdsuse (===) abil ja uus funktsioon on alati eelmisest erinev.
Näide:
import React, { useState, useCallback } from 'react';
const Button = React.memo(({ onClick, children }) => {
console.log('Nupp renderdati');
return ;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// Memoiseeri inkremendi funktsioon
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // Tühi sõltuvuste massiiv tähendab, et see funktsioon luuakse ainult üks kord
return (
Loendur: {count}
);
}
export default ParentComponent;
Selles näites on increment funktsioon memoiseeritud useCallback'iga, millel on tühi sõltuvuste massiiv. See tähendab, et funktsioon luuakse ainult üks kord, kui komponent paigaldatakse. Kuna Button komponent on mähitud React.memo'sse, renderdatakse see uuesti ainult siis, kui selle props'id muutuvad. Kuna increment funktsioon on igal renderdamisel sama, ei renderdata Button komponenti mittevajalikult uuesti.
Optimeerimisstrateegia 5: React.memo kasutamine funktsionaalsete komponentide jaoks
React.memo on kõrgema järgu komponent, mis memoiseerib funktsionaalseid komponente. See takistab komponendi uuesti renderdamist, kui selle props'id pole muutunud. See on eriti kasulik puhaste komponentide jaoks, mis sõltuvad ainult oma props'idest.
Näide:
import React from 'react';
const MyComponent = React.memo(({ name }) => {
console.log('MyComponent renderdati');
return Tere, {name}!
;
});
export default MyComponent;
React.memo tõhusaks kasutamiseks veenduge, et teie komponent on puhas, mis tähendab, et see renderdab alati sama väljundi samade sisend-props'ide korral. Kui teie komponendil on kõrvalmõjusid või see tugineb context'ile, mis võib muutuda, ei pruugi React.memo olla parim lahendus.
Optimeerimisstrateegia 6: Suurte komponentide tĂĽkeldamine
Suured komponendid keeruka olekuga võivad muutuda jõudluse kitsaskohtadeks. Nende komponentide tükeldamine väiksemateks, paremini hallatavateks osadeks võib parandada jõudlust, isoleerides uuesti renderdamisi. Kui üks osa rakenduse olekust muutub, peab uuesti renderdama ainult asjakohane alamkomponent, mitte kogu suur komponent.
Näide (kontseptuaalne):
Selle asemel, et omada ĂĽhte suurt UserProfile komponenti, mis haldab nii kasutajateavet kui ka tegevuste voogu, tĂĽkeldage see kaheks komponendiks: UserInfo ja ActivityFeed. Iga komponent haldab oma olekut ja renderdatakse uuesti ainult siis, kui selle konkreetsed andmed muutuvad.
Optimeerimisstrateegia 7: Reducer'ite kasutamine useReducer'iga keeruka olekuloogika jaoks
Keerukate olekuüleminekutega tegelemisel võib useReducer olla võimas alternatiiv useState'ile. See pakub struktureeritumat viisi oleku haldamiseks ja võib sageli viia parema jõudluseni. useReducer hook haldab keerukat olekuloogikat, sageli mitme alamväärtusega, mis vajab tegevustel põhinevaid detailseid uuendusi.
Näide:
import React, { useReducer } from 'react';
const initialState = { count: 0, theme: 'light' };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'toggleTheme':
return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
Loendur: {state.count}
Teema: {state.theme}
);
}
export default Counter;
Selles näites haldab reducer funktsioon erinevaid tegevusi, mis uuendavad olekut. useReducer võib samuti aidata renderdamise optimeerimisel, sest saate memoiseerimisega kontrollida, millised oleku osad põhjustavad komponentide renderdamist, võrreldes potentsiaalselt laialdasemate uuesti renderdamistega, mida põhjustavad paljud `useState` hook'id.
Optimeerimisstrateegia 8: Valikulised oleku uuendused
Mõnikord võib teil olla komponent mitme olekumuutujaga, kuid ainult mõned neist käivitavad muutumisel uuesti renderdamise. Sellistel juhtudel saate olekut valikuliselt uuendada, kasutades mitut useState hook'i. See võimaldab teil isoleerida uuesti renderdamised ainult nendele komponendi osadele, mida on tegelikult vaja uuendada.
Näide:
import React, { useState } from 'react';
function MyComponent() {
const [name, setName] = useState('John');
const [age, setAge] = useState(30);
const [location, setLocation] = useState('New York');
// Uuenda asukohta ainult siis, kui asukoht muutub
const handleLocationChange = (newLocation) => {
setLocation(newLocation);
};
return (
Nimi: {name}
Vanus: {age}
Asukoht: {location}
);
}
export default MyComponent;
Selles näites renderdab location'i muutmine uuesti ainult selle osa komponendist, mis kuvab location'it. name'i ja age'i olekumuutujad ei põhjusta komponendi uuesti renderdamist, kui neid pole selgesõnaliselt uuendatud.
Optimeerimisstrateegia 9: Oleku uuenduste Debouncing ja Throttling
Olukordades, kus oleku uuendusi käivitatakse sageli (nt kasutaja sisestuse ajal), võivad debouncing ja throttling aidata vähendada uuesti renderdamiste arvu. Debouncing lükkab funktsiooni kutse edasi, kuni on möödunud teatud aeg viimasest funktsiooni kutsumisest. Throttling piirab, mitu korda saab funktsiooni teatud aja jooksul kutsuda.
Näide (Debouncing):
import React, { useState, useCallback } from 'react';
import debounce from 'lodash.debounce'; // Paigalda lodash: npm install lodash
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSetSearchTerm = useCallback(
debounce((text) => {
setSearchTerm(text);
console.log('Otsingusõna uuendatud:', text);
}, 300),
[]
);
const handleInputChange = (event) => {
debouncedSetSearchTerm(event.target.value);
};
return (
Otsin: {searchTerm}
);
}
export default SearchComponent;
Selles näites kasutatakse Lodashi debounce funktsiooni, et lükata setSearchTerm funktsiooni kutset 300 millisekundi võrra edasi. See takistab oleku uuendamist igal klahvivajutusel, vähendades uuesti renderdamiste arvu.
Optimeerimisstrateegia 10: useTransition kasutamine mitteblokeerivate kasutajaliidese uuenduste jaoks
Ülesannete puhul, mis võivad blokeerida põhilõime ja põhjustada kasutajaliidese hangumist, saab useTransition hook'i kasutada oleku uuenduste märkimiseks mittekiireloomulisteks. React seab seejärel esikohale muud ülesanded, näiteks kasutaja interaktsioonid, enne mittekiireloomuliste oleku uuenduste töötlemist. See tagab sujuvama kasutajakogemuse isegi arvutusmahukate operatsioonidega tegelemisel.
Näide:
import React, { useState, useTransition } from 'react';
function MyComponent() {
const [isPending, startTransition] = useTransition();
const [data, setData] = useState([]);
const loadData = () => {
startTransition(() => {
// Simuleeri andmete laadimist API-st
setTimeout(() => {
setData([1, 2, 3, 4, 5]);
}, 1000);
});
};
return (
{isPending && Andmete laadimine...
}
{data.length > 0 && Andmed: {data.join(', ')}
}
);
}
export default MyComponent;
Selles näites kasutatakse startTransition funktsiooni, et märkida setData kutse mittekiireloomuliseks. React seab seejärel esikohale muud ülesanded, näiteks kasutajaliidese uuendamise, et kajastada laadimise olekut, enne oleku uuenduse töötlemist. isPending lipp näitab, kas üleminek on pooleli.
Täiendavad kaalutlused: Context ja globaalne olekuhaldus
Keerukate rakenduste puhul, kus on jagatud olek, kaaluge React Contexti või globaalse olekuhalduse teegi, nagu Redux, Zustand või Jotai, kasutamist. Need lahendused võivad pakkuda tõhusamaid viise oleku haldamiseks ja vältida mittevajalikke uuesti renderdamisi, võimaldades komponentidel tellida ainult neid konkreetseid oleku osi, mida nad vajavad.
Kokkuvõte
useState'i optimeerimine on oluline jõudlusega ja hooldatavate Reacti rakenduste ehitamiseks. Mõistes olekuhalduse nüansse ja rakendades selles juhendis kirjeldatud tehnikaid, saate oma Reacti rakenduste jõudlust ja reageerimisvõimet märkimisväärselt parandada. Ärge unustage oma rakendust profileerida, et tuvastada jõudluse kitsaskohti ja valida optimeerimisstrateegiad, mis sobivad teie konkreetsetele vajadustele kõige paremini. Ärge optimeerige ennatlikult, ilma tegelikke jõudlusprobleeme tuvastamata. Keskenduge esmalt puhta ja hooldatava koodi kirjutamisele ning seejärel optimeerige vastavalt vajadusele. Võti on leida tasakaal jõudluse ja koodi loetavuse vahel.